在版本 7.1 之後, React Redux 也加入了 Hooks,簡化了以往需要透過 connect
方法並傳入 mapStateToProps
與 mapStateToDispatch
的方式才可以操作 Redux 的 store 物件。
而今天要來看看怎麼使用,也會看看在文件中的定義。
在使用 Hooks 之前需要先完成一些前置作業,這個部分其實也很簡單,就是建立一個 store 並將 store 物件透過 Provider
提供給 App
元件使用。
這邊參考官方提供的範例,基本上相差不多:
const store = createStore(rootReducer)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
接著第一個要學習的是 useSelector
,這個方法允許我們直接從 Redux store 中的狀態提取數據到元件中。
透過 useSelector
可以取代掉 mapStateToProps
的使用方式
在之前如果我們需要從 store 中拿到某個狀態的數據會是這麼寫:
// ...略
const mapStateToProps = (state) => {
return {
counter: state.counter
};
};
export default connect(mapStateToProps)(App)
但有了 Hooks,我們可以改寫成這樣:
import React from 'react'
import { useSelector } from 'react-redux'
export const CounterComponent = () => {
const counter = useSelector(state => state.counter)
return <div>{counter}</div>
}
而在使用上基本上 mapStateToProps
大致相同於 useSelector
,但依然有些差異,以下擷取至文件中的段落:
useSelector
可以回傳任何值,並不一定是一個物件(mapStateToProps
則是必定回傳一個物件)。useSelector
會將前一個結果與當前的結果進行比較,如果不同就會強制更新元件,不然就不會更新元件。useSelector
沒有自己的 props
,但可以透過 JavaScript 的閉包觀念取得元件中的 props
。useSelector
預設是使用 ===
嚴格等於的方式檢查(mapStateToProps
則是 ==
)然後要學習的是 useDispatch
,對於 useSelector
有了一些認識就知道這個方法要用來做什麼
沒錯,可以用來取代掉 mapStateToDispatch
!!
而看到這裡應該不難發現,使用了 Hooks 之後,我們基本上就要跟 connect
、 mapStateToProps
與 mapStateToDispatch
說再見啦,完全用不到了
而之前如果我們需要發一個 action
到 Redux 的 Reducer 中更新 state 的時候會是這麼寫:
// ...略
const mapDispatchToProps = (dispatch) => {
return {
incrementHandler: () => dispatch({ type: "INCREMENTHANDLER" })
};
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
但現在透過 Hooks 的話,可以這麼寫:
import React from 'react'
import { useDispatch } from 'react-redux'
export const CounterComponent = ({ value }) => {
const dispatch = useDispatch();
return (
<div>
<span>{value}</span>
<button onClick={() => dispatch({ type: 'INCREMENTHANDLER' } )}>
Increment counter
</button>
</div>
)
而這邊需要注意的是在文件中有提到如果我們是透過 callback 的方式來發一個 action
時,需要透過 useCallback
的方式處理,避免掉多餘的重新渲染。
所以我們把上面的程式碼修改如下:
import React from 'react'
import { useDispatch } from 'react-redux'
export const CounterComponent = ({ value }) => {
const dispatch = useDispatch();
const incrementHandler = useCallback(
() => dispatch({ type: 'INCREMENTHANDLER' }),
[dispatch]
);
return (
<div>
<span>{value}</span>
<button onClick={() => incrementHandler )}>
Increment counter
</button>
</div>
)
}
最後要學的是 useStore
這個方法,這個方法可以讓我們取得我們透過 Provider
提供的 store 物件。
來看看官方提供的程式碼:
import React from 'react'
import { useStore } from 'react-redux'
export const CounterComponent = ({ value }) => {
const store = useStore()
// EXAMPLE ONLY! Do not do this in a real app.
// The component will not automatically update if the store state changes
return <div>{store.getState()}</div>
}
而在篇幅的最後,這裡要改寫第23天的測試範例
這邊擷取出局部程式碼:
相關測試範例,點擊前往。
import React, { useCallback } from "react";
import { useSelector, useDispatch } from "react-redux";
import "./styles.css";
const App = () => {
const dispatch = useDispatch();
const number = useSelector((state) => state.number);
const incrementHandler = useCallback(
() => dispatch({ type: "INCREMENTHANDLER" }),
[dispatch]
);
const decrementHandler = useCallback(
() => dispatch({ type: "DECREMENTHANDLER" }),
[dispatch]
);
const incrementTenNumberHandler = useCallback(
(number) =>
dispatch({
type: "INCREMENTTENNUMBERHANDLER",
payload: { number }
}),
[dispatch]
);
return (
<div className="App">
<h2>使用 React Redux Hooks 改寫</h2>
<h2>數字: {number}</h2>
<button onClick={incrementHandler}>點擊 + 1</button>
<button onClick={decrementHandler}>點擊 - 1</button>
<button
onClick={() => {
incrementTenNumberHandler(number);
}}
>
由當前數字 + 10
</button>
</div>
);
};
export default App;
上面的測試範例是簡單的加減計數器,這裡透過 useSelector
取得 store 物件中的 number
狀態的數據,並透過 useDispatch
來 dispatch
對應的 action 到 Reducer 中,並執行後續的 state 更新。
鐵人賽文章與程式碼同步發佈於: